import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:todo_app/utils/call/CallHelper.dart';

LocalRtcVideoController useLocalRtcVideoController() {
  return use(const _LocalRtcVideoControllerHook());
}

class _LocalRtcVideoControllerHook extends Hook<LocalRtcVideoController> {
  const _LocalRtcVideoControllerHook();

  @override
  HookState<LocalRtcVideoController, Hook<LocalRtcVideoController>> createState() {
    return _LocalRtcVideoControllerHookState();
  }
}

class _LocalRtcVideoControllerHookState extends HookState<LocalRtcVideoController, _LocalRtcVideoControllerHook> {
  late LocalRtcVideoController controller;

  @override
  void initHook() {
    super.initHook();
    controller = LocalRtcVideoController();
    controller.setState = (VoidCallback fn) {
      try {
        setState(fn);
      } catch (e) {
        print(e); 
      }
    };
    controller.init();
  }

  Future hangUp() async {
    controller.inCalling = false;
    controller.dispose();
  }

  @override
  LocalRtcVideoController build(BuildContext context) {
    return controller;
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }
}

typedef SetStateFunction = void Function(VoidCallback fn);

class LocalRtcVideoController {
  MediaStream? stream;
  final renderer = RTCVideoRenderer();
  List<MediaDeviceInfo>? mediaDevices;
  bool inCalling = false;
  bool isTorchOn = false;
  bool isMini = false;
  late SetStateFunction setState;
  VoidCallback? onInited;

  MediaRecorder? mediaRecorder;

  Future getUserMedia() async {
    navigator.mediaDevices.ondevicechange = (event) async {
      mediaDevices = await navigator.mediaDevices.enumerateDevices();
      if(mediaDevices != null && mediaDevices!.isNotEmpty == true) {
        var deviceId = mediaDevices!.first.deviceId;
        audioOutput(deviceId);
      }
    };
    final mediaConstraints = <String, dynamic>{
      'audio': true,
      'video': {
        'mandatory': {
          'minWidth': '640',
          'minHeight': '480',
          'minFrameRate': '30',
        },
        'facingMode': 'user',
        'optional': [],
      }
    };

    try {
      stream = await navigator.mediaDevices.getUserMedia(mediaConstraints);
      mediaDevices = await navigator.mediaDevices.enumerateDevices();
      if(mediaDevices != null && mediaDevices!.isNotEmpty == true) {
        var deviceId = mediaDevices!.first.deviceId;
        audioOutput(deviceId);
      }
      renderer.srcObject = stream;
    } catch (e) {
      print(e.toString());
    }
    inCalling = true;
    setState((){});
  }

  Future init() async {
    await renderer.initialize();
    setState((){});
    if(onInited != null) onInited!();
  }

  Future stopStream() async {
    try {
      await Future.forEach(stream?.getTracks() ?? [], (track) async {
        await track.stop();
      });
      await stream?.dispose();
      stream = null;
      renderer.srcObject = null;
    } catch (e) {
      print(e.toString());
    }
  }

  Future dispose() async {
    try {
      await stopStream();
      await renderer.dispose();
      navigator.mediaDevices.ondevicechange = null;
    } catch (e) {
      print(e); 
    }
    setState((){});
  }

  Future<bool> audioOutput(String deviceId) async {
    var flag = await renderer.audioOutput(deviceId);
    setState((){});
    return flag;
  }

  Future<bool> toggleTorch() async {
    if (stream == null) throw Exception('Stream is not initialized');
    final videoTrack = stream!
        .getVideoTracks()
        .firstWhere((track) => track.kind == 'video');
    final has = await videoTrack.hasTorch();
    if (has) {
      isTorchOn != isTorchOn;
      await videoTrack.setTorch(isTorchOn);
      return true;
    }
    setState((){});
    return has;
  }

  Future<bool> captureFrame(BuildContext context) async {
    if (stream == null) return false;
    final videoTrack = stream!.getVideoTracks().firstWhere((track) => track.kind == 'video');
    final frame = await videoTrack.captureFrame();
    setState((){});
    if(context.mounted) {
    await showDialog(
        context: context,
        builder: (context) => AlertDialog(
          content: Image.memory(frame.asUint8List(), height: 1280, width: 720),
          actions: <Widget>[
            TextButton(
              onPressed: Navigator.of(context, rootNavigator: true).pop,
              child: const Text('确定'),
            )
          ],
        )
      );
    }
    return true;
  }

  Future<bool> toggleCamera() async {
    if (stream == null) return false;
    final videoTrack = stream!
        .getVideoTracks()
        .firstWhere((track) => track.kind == 'video');
    await Helper.switchCamera(videoTrack);
    setState((){});
    return true;
  }

  void toggleMiniUi() {
    isMini != isMini;
    print(isMini);
    setState((){});
    CallHelper.setUiChange();
  }
}